#include "sdk.h"
#include "crc32.h"
#include <wx/url.h>
#include <wx/stream.h>
#include <wx/stdpaths.h>
#include <wx/filename.h>
#include "tinywxuni.h"
#include <stdlib.h>
#include <glib.h>

#define CompilerPluginFilename   _T("libCompiler.so")

template<> CfgMgrBldr* Mgr<CfgMgrBldr>::instance = nullptr;
template<> bool  Mgr<CfgMgrBldr>::isShutdown = false;

wxString ConfigManager::alternate_user_data_path;
bool ConfigManager::has_alternate_user_data_path = false;
wxString ConfigManager::config_folder;
wxString ConfigManager::home_folder;
wxString ConfigManager::data_path_user;
wxString ConfigManager::data_path_global;
wxString ConfigManager::app_path;
wxString ConfigManager::temp_folder;

namespace CfgMgrConsts {
  const wxString app_path( _T( "app_path" ) );
  const wxString data_path( _T( "data_path" ) );
  const wxString dotDot( _T( ".." ) );
  const int version = 1;
}

namespace {
  wxString DetermineExecutablePath() {
    char c[PATH_MAX + 1];
    char *p = realpath( "/proc/self/exe", &c[0] );
    if( p == nullptr ) {
      return _T( "." );
    }
    wxFileName fname( cbC2U( p ) );
    return fname.GetPath( wxPATH_GET_VOLUME );
  }

  wxString DetermineResourcesPath() {
    return _T( "." );
  }
}

inline void ConfigManager::Collapse( wxString& str ) const {
  const wxChar *src = str.c_str();
  wxChar *dst = ( wxChar* ) src;
  wxChar c;
  size_t len = 0;
  while( ( c = *src ) ) {
    ++src;
    *dst = c;
    ++dst;
    ++len;
    if( c == _T( '/' ) )
      while( *src == _T( '/' ) ) {
        ++src;
      }
  }
  str.Truncate( len );
}

ISerializable::ISerializable() {}
ISerializable::~ISerializable() {}

CfgMgrBldr::CfgMgrBldr() : doc( nullptr ), volatile_doc( nullptr ), r( false ) {
  ConfigManager::MigrateFolders();
  TiXmlBase::SetCondenseWhiteSpace( false );
  wxString personality( Manager::Get()->GetPersonalityManager()->GetPersonality() );
  if( personality.StartsWith( _T( "http://" ) ) ) {
    SwitchToR( personality );
    return;
  }
  cfg = FindConfigFile( personality + _T( ".conf" ) );
  if( cfg.IsEmpty() ) {
    cfg = ConfigManager::GetConfigFolder() + wxFILE_SEP_PATH + personality + _T( ".conf" );
    Manager::Get()->GetLogManager()->LogInf( F( _T( "创建配置文件路径为:  %s " ), cfg.wx_str() ) );
    doc = new TiXmlDocument();
    doc->InsertEndChild( TiXmlDeclaration( "UTF-8", "yes" ) );
    doc->InsertEndChild( TiXmlElement( "m_Code_Config" ) );
    return;
  } else {
    Manager::Get()->GetLogManager()->LogInf( F( _T( "配置文件路径为:  %s " ), cfg.wx_str() ) );
  }
  SwitchTo( cfg );
}

wxString CfgMgrBldr::FindConfigFile( const wxString& filename ) {
  wxString u( ConfigManager::GetUserDataFolder() + wxFILE_SEP_PATH + filename );
  wxString exePath( ::DetermineExecutablePath() );
  wxString e( exePath + wxFILE_SEP_PATH + filename );
  if( !ConfigManager::has_alternate_user_data_path && ::wxFileExists( e ) ) {
    ConfigManager::SetUserDataFolder( exePath );
    return e;
  }
  if( ::wxFileExists( u ) ) {
    return u;
  }
  return wxEmptyString;
}

static void handleConfigError( TiXmlDocument &doc, const wxString &fileName, const wxString &additionalMessage ) {
  wxString message;
  if( doc.ErrorId() ) {
    message = F( _T( "TinyXML error: %s\nIn file: %s\nAt row %d, column: %d.\n\n" ),
                 cbC2U( doc.ErrorDesc() ).c_str(), fileName.wx_str(),
                 doc.ErrorRow(), doc.ErrorCol() );
  }
  message += additionalMessage;
  wxMessageDialog dlg( Manager::Get()->GetAppWindow(),
                       message + _T( "\n\n放弃旧配置文件?" ), _T( "配置文件读取错误" ),
                       wxSTAY_ON_TOP | wxCENTRE | wxYES | wxNO | wxNO_DEFAULT | wxICON_ERROR );
  if( dlg.ShowModal() != wxID_YES ) {
    m_Throw( message );
  }
  doc.ClearError();
  doc.InsertEndChild( TiXmlDeclaration( "UTF-8", "yes" ) );
  doc.InsertEndChild( TiXmlElement( "m_Code_Config" ) );
}

void CfgMgrBldr::SwitchTo( const wxString& fileName ) {
  doc = new TiXmlDocument();
  if( !TinyXML::LoadDocument( fileName, doc ) ) {
    const wxString message = F( _T( "读取配置文件时出错: %s" ), fileName.wx_str() );
    handleConfigError( *doc, fileName, message );
  }
  TiXmlElement* docroot = doc->FirstChildElement( "m_Code_Config" );
  if( !docroot ) {
    const wxString message = F( _T( "无效的配置文件 '%s'" ), fileName.wx_str() );
    handleConfigError( *doc, fileName, message );
    m_Throw( message );
  }
  doc->ClearError();
  TiXmlNode *firstchild = docroot->FirstChild();
  if( firstchild && firstchild->ToComment() ) {
    docroot->RemoveChild( firstchild );
    firstchild = docroot->FirstChild();
  }
}

void CfgMgrBldr::SwitchToR( const wxString& absFileName ) {
  if( doc ) {
    delete doc;
  }
  doc = new TiXmlDocument();
  doc->ClearError();
  cfg = absFileName;
  wxURL url( absFileName );
  url.SetProxy( ConfigManager::GetProxy() );
  if( url.GetError() == wxURL_NOERR ) {
    wxInputStream *is = url.GetInputStream();
    if( is && is->IsOk() ) {
      size_t size = is->GetSize();
      wxString str;
      wxChar* c = str.GetWriteBuf( size );
      is->Read( c, size );
      str.UngetWriteBuf( size );
      doc = new TiXmlDocument();
      if( doc->Parse( cbU2C( str ) ) ) {
        doc->ClearError();
        delete is;
        return;
      }
      if( Manager::Get()->GetLogManager() ) {
        Manager::Get()->GetLogManager()->DebugLog( _T( "##### 加载或分析远程配置文件时出错" ) );
        Manager::Get()->GetLogManager()->DebugLog( cbC2U( doc->ErrorDesc() ) );
        doc->ClearError();
      }
    }
    delete is;
  }
  cfg.Empty();
  SwitchTo( wxEmptyString );
}

CfgMgrBldr::~CfgMgrBldr() {
  NamespaceMap::iterator it;
  for( it = namespaces.begin(); it != namespaces.end(); ++it ) {
    delete it->second;
  }
  namespaces.clear();
  Close();
  delete volatile_doc;
}

void CfgMgrBldr::Flush() {
  int lock = 0;
  if( doc ) {
    if( !cfg.StartsWith( _T( "http://" ) ) ) {
      bool done = false;
      do {
        if( TinyXML::SaveDocument( cfg, doc ) ) {
          done = true;
        } else {
          Manager::Get()->GetLogManager()->LogInf( F( _T( "正在尝试第 %d 次保存" ), lock++ ) );
          done = false;
          if( lock >= 3 ) {
            msging( F( _T( "无法保存配置文件'%s'!" ), cfg.wx_str() ), _( "错误" ) );
            done = true;
            lock = 0;
          }
        }
      } while( !done );
    }
  }
}

void CfgMgrBldr::Close() {
  Flush();
  if( doc ) {
    delete doc;
  }
  doc = nullptr;
}

ConfigManager* CfgMgrBldr::GetConfigManager( const wxString& name_space ) {
  return Get()->Build( name_space );
}

ConfigManager* CfgMgrBldr::Build( const wxString& name_space ) {
  if( name_space.IsEmpty() ) {
    m_Throw( _T( "您试图在不提供命名空间的情况下获取ConfigManager实例." ) );
  }
  wxCriticalSectionLocker locker( cs );
  NamespaceMap::iterator it = namespaces.find( name_space );
  if( it != namespaces.end() ) {
    return it->second;
  }
  TiXmlElement* docroot;
  if( name_space.StartsWith( _T( "volatile:" ) ) ) {
    if( !volatile_doc ) {
      volatile_doc = new TiXmlDocument();
      volatile_doc->InsertEndChild( TiXmlElement( "m_Code_Config" ) );
      volatile_doc->SetCondenseWhiteSpace( false );
    }
    docroot = volatile_doc->FirstChildElement( "m_Code_Config" );
  } else {
    docroot = doc->FirstChildElement( "m_Code_Config" );
    if( !docroot ) {
      wxString err( _T( "分析提供的配置文件时出现致命错误.\n分析器错误消息:\n" ) );
      err << F( _T( "%s\n在行 %d, 列: %d." ), cbC2U( doc->ErrorDesc() ).c_str(), doc->ErrorRow(), doc->ErrorCol() );
      m_Throw( err );
    }
  }
  TiXmlElement* root = docroot->FirstChildElement( cbU2C( name_space ) );
  if( !root ) {
    docroot->InsertEndChild( TiXmlElement( cbU2C( name_space ) ) );
    root = docroot->FirstChildElement( cbU2C( name_space ) );
  }
  if( !root ) {
    m_Throw( _T( "无法在文档树中创建命名空间（实际上不可能?)" ) );
  }
  ConfigManager *c = new ConfigManager( root );
  namespaces[name_space] = c;
  return c;
}

inline void to_upper( wxString& s ) {
  wxChar *p = ( wxChar* ) s.c_str();
  wxChar q;
  size_t len = s.length() + 1;
  for( ; --len; ++p ) {
    q = *p;
    if( q >= 'a' && q <= 'z' ) {
      *p = q - 32;
    }
  }
}

inline void to_lower( wxString& s ) {
  wxChar *p = ( wxChar* ) s.c_str();
  wxChar q;
  size_t len = s.length() + 1;
  for( ; --len; ++p ) {
    q = *p;
    if( q >= 'A' && q <= 'Z' ) {
      *p = q + 32;
    }
  }
}

wxString ConfigManager::GetProxy() {
  return Manager::Get()->GetConfigManager( _T( "app" ) )->Read( _T( "network_proxy" ) );
}

wxString ConfigManager::GetFolder( SearchDirs dir ) {
  static bool once = 1;
  if( once ) {
    InitPaths();
    once = false;
  }
  switch( dir ) {
    case sdHome:
      return ConfigManager::home_folder;
    case sdBase:
      return ConfigManager::app_path;
    case sdTemp:
      return ConfigManager::temp_folder;
    case sdConfig:
      return ConfigManager::config_folder;
    case sdCurrent:
      return ::wxGetCwd();
    case sdPluginsGlobal:
      return ConfigManager::data_path_global + wxFILE_SEP_PATH + _T( "plugins" );
    case sdPluginsUser:
      return ConfigManager::data_path_user   + wxFILE_SEP_PATH + _T( "plugins" );
    case sdScriptsGlobal:
      return ConfigManager::data_path_global + wxFILE_SEP_PATH + _T( "scripts" );
    case sdScriptsUser:
      return ConfigManager::data_path_user   + wxFILE_SEP_PATH + _T( "scripts" );
    case sdDataGlobal:
      return ConfigManager::data_path_global;
    case sdDataUser:
      return ConfigManager::data_path_user;
    case sdPath:
    case sdAllUser:
    case sdAllGlobal:
    case sdAllKnown:
    default:
      return wxEmptyString;
  }
}

inline wxString ConfigManager::GetUserDataFolder() {
  if( has_alternate_user_data_path ) {
    return alternate_user_data_path;
  }
  return wxString::FromUTF8( g_build_filename( g_get_user_config_dir(), "m", NULL ) );
}

bool ConfigManager::SetUserDataFolder( const wxString &user_data_path ) {
  wxString udp = wxFileName::DirName( user_data_path ).GetFullPath();
  if( !CreateDirRecursively( udp ) ) {
    InfoWindow::Tip( F( _T( "--user-data-dir 目录 %s 不存在无法创建 检查路径后重试" ), user_data_path.c_str() ), _( "错误" ), 3500 );
    return false;
  }
  has_alternate_user_data_path = true;
  ConfigManager::alternate_user_data_path = udp;
  return true;
}

wxString ConfigManager::LocateDataFile( const wxString& filename, int search_dirs ) {
  wxPathList searchPaths;
  if( search_dirs & sdPluginsUser ) {
    searchPaths.Add( GetPluginsFolder( false ) );
  }
  if( search_dirs & sdScriptsUser ) {
    searchPaths.Add( GetScriptsFolder( false ) );
  }
  if( search_dirs & sdDataUser ) {
    searchPaths.Add( GetDataFolder( false ) );
  }
  if( search_dirs & sdPluginsGlobal ) {
    searchPaths.Add( GetPluginsFolder( true ) );
  }
  if( search_dirs & sdScriptsGlobal ) {
    searchPaths.Add( GetScriptsFolder( true ) );
  }
  if( search_dirs & sdDataGlobal ) {
    searchPaths.Add( GetDataFolder( true ) );
  }
  if( search_dirs & sdCurrent ) {
    searchPaths.Add( ::wxGetCwd() );
  }
  if( search_dirs & sdConfig ) {
    searchPaths.Add( GetConfigFolder() );
  }
  if( search_dirs & sdHome ) {
    searchPaths.Add( GetHomeFolder() );
  }
  if( search_dirs & sdBase ) {
    searchPaths.Add( GetExecutableFolder() );
  }
  if( search_dirs & sdTemp ) {
    searchPaths.Add( GetTempFolder() );
  }
  if( search_dirs & sdPath ) {
    searchPaths.AddEnvList( _T( "PATH" ) );
  }
  return searchPaths.FindValidPath( filename );
}

ConfigManager::ConfigManager( TiXmlElement* r ) : doc( r->GetDocument() ), root( r ), pathNode( r ) {
}

wxString ConfigManager::GetPath() const {
  TiXmlElement *e = pathNode;
  wxString ret;
  ret.Alloc( 64 );
  ret = cbC2U( e->Value() );
  while( ( e = e->Parent()->ToElement() ) && e != root ) {
    ret.Prepend( _T( '/' ) );
    ret.Prepend( cbC2U( e->Value() ) );
  }
  ret.Prepend( _T( '/' ) );
  return ret;
}

void ConfigManager::SetPath( const wxString& path ) {
  wxString p( path + _T( '/' ) );
  pathNode = AssertPath( p );
}

wxString ConfigManager::InvalidNameMessage( const wxString& what, const wxString& sub, TiXmlElement *localPath ) const {
  wxString s;
  s.Printf( _T( "%s 发起 %s 值写入时不符合命名标准 必须以字母开头(在根节点 \"%s\" 中的子节点 \"%s\") " ),
            what.c_str(), sub.c_str(), cbC2U( root->Value() ).c_str(),
            cbC2U( localPath->Value() ).c_str() );
  return s;
}

TiXmlElement* ConfigManager::AssertPath( wxString& path ) {
  Collapse( path );
  wxString illegal( _T( " -:.\"\'$&()[]<>+#" ) );
  size_t i = 0;
  while( ( i = path.find_first_of( illegal, i ) ) != wxString::npos ) {
    path[i] = _T( '_' );
  }
  TiXmlElement *localPath = pathNode ? pathNode : root;
  if( path.GetChar( 0 ) == '/' ) {
    localPath = root;
    path = path.Mid( 1 );
  }
  if( path.find( _T( '/' ) ) != wxString::npos ) {
    to_lower( path );
  }
  wxString sub;
  while( path.find( _T( '/' ) ) != wxString::npos ) {
    sub = path.BeforeFirst( _T( '/' ) );
    path = path.AfterFirst( _T( '/' ) );
    if( localPath != root && sub.IsSameAs( CfgMgrConsts::dotDot ) ) {
      localPath = localPath->Parent()->ToElement();
    } else if( sub.GetChar( 0 ) < _T( 'a' ) || sub.GetChar( 0 ) > _T( 'z' ) ) {
      m_Throw( InvalidNameMessage( _T( "subpath" ), sub, localPath ) );
    } else {
      TiXmlElement* n = localPath->FirstChildElement( cbU2C( sub ) );
      if( n ) {
        localPath = n;
      } else
      { localPath = ( TiXmlElement* ) localPath->InsertEndChild( TiXmlElement( cbU2C( sub ) ) ); }
    }
  }
  to_upper( path );
  if( !path.IsEmpty() && ( path.GetChar( 0 ) < _T( 'A' ) || path.GetChar( 0 ) > _T( 'Z' ) ) ) {
    m_Throw( InvalidNameMessage( _T( "key" ), path, localPath ) );
  }
  return localPath;
}

void ConfigManager::Clear() {
  root->Clear();
}

void ConfigManager::Delete() {
  CfgMgrBldr * bld = CfgMgrBldr::Get();
  const wxString ns( cbC2U( root->Value() ) );
  root->Clear();
  doc->RootElement()->RemoveChild( root );
  wxCriticalSectionLocker( bld->cs );
  NamespaceMap::iterator it = bld->namespaces.find( ns );
  if( it != bld->namespaces.end() ) {
    bld->namespaces.erase( it );
  }
  delete this;
}

void ConfigManager::DeleteAll() {
  CfgMgrBldr * bld = CfgMgrBldr::Get();
  const wxString ns( cbC2U( root->Value() ) );
  if( !ns.IsSameAs( _T( "app" ) ) ) {
    m_Throw( _T( "非法尝试调用 DeleteAll()." ) );
  }
  wxCriticalSectionLocker( bld->cs );
  doc->RootElement()->Clear();
  for( NamespaceMap::iterator it = bld->namespaces.begin(); it != bld->namespaces.end(); ++it ) {
    delete it->second;
  }
  bld->namespaces.clear();
}

void ConfigManager::Flush() {
  CfgMgrBldr * bld = CfgMgrBldr::Get();
  wxCriticalSectionLocker( bld->cs );
  bld->Flush();
}

TiXmlElement* ConfigManager::GetUniqElement( TiXmlElement* p, const wxString& q ) {
  TiXmlElement* r;
  if( ( r = p->FirstChildElement( cbU2C( q ) ) ) ) {
    return r;
  }
  return ( TiXmlElement* )( p->InsertEndChild( TiXmlElement( cbU2C( q ) ) ) );
}

void ConfigManager::SetNodeText( TiXmlElement* n, const TiXmlText& t ) {
  TiXmlNode *c = n->FirstChild();
  if( c ) {
    n->ReplaceChild( c, t );
  } else {
    n->InsertEndChild( t );
  }
}

void ConfigManager::Write( const wxString& name, const wxString& value, bool ignoreEmpty ) {
  if( name.IsSameAs( CfgMgrConsts::app_path ) ) {
    return;
  } else if( name.IsSameAs( CfgMgrConsts::data_path ) ) {
    data_path_global = value;
    return;
  }
  if( ignoreEmpty && value.IsEmpty() ) {
    UnSet( name );
    return;
  }
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlElement *str = GetUniqElement( e, key );
  TiXmlElement *s = GetUniqElement( str, _T( "str" ) );
  TiXmlText t( value.mb_str( wxConvUTF8 ) );
  t.SetCDATA( true );
  SetNodeText( s, t );
}

void ConfigManager::Write( const wxString& key, const char* str ) {
  Write( key, cbC2U( str ), false );
}

wxString ConfigManager::Read( const wxString& name, const wxString& defaultVal ) {
  if( name.IsSameAs( CfgMgrConsts::app_path ) ) {
    return app_path;
  } else if( name.IsSameAs( CfgMgrConsts::data_path ) ) {
    return data_path_global;
  }
  wxString ret;
  if( Read( name, &ret ) ) {
    return ret;
  } else {
    return defaultVal;
  }
}

bool ConfigManager::Read( const wxString& name, wxString* str ) {
  if( name.IsSameAs( CfgMgrConsts::app_path ) ) {
    str->assign( app_path );
    return true;
  } else if( name.IsSameAs( CfgMgrConsts::data_path ) ) {
    str->assign( data_path_global );
    return true;
  }
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlHandle parentHandle( e );
  TiXmlText *t = ( TiXmlText * ) parentHandle.FirstChild( cbU2C( key ) ).FirstChild( "str" ).FirstChild().Node();
  if( t ) {
    str->assign( cbC2U( t->Value() ) );
    return true;
  }
  return false;
}

void ConfigManager::Write( const wxString& name,  const wxColour& c ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlElement *leaf = GetUniqElement( e, key );
  TiXmlElement *s = GetUniqElement( leaf, _T( "colour" ) );
  if( c == wxNullColour ) {
    s->SetAttribute( "null", "true" );
    s->SetAttribute( "r", 0 );
    s->SetAttribute( "g", 0 );
    s->SetAttribute( "b", 0 );
  } else {
    s->SetAttribute( "r", c.Red() );
    s->SetAttribute( "g", c.Green() );
    s->SetAttribute( "b", c.Blue() );
  }
}

wxColour ConfigManager::ReadColour( const wxString& name, const wxColour& defaultVal ) {
  wxColour ret;
  if( Read( name, &ret ) ) {
    return ret;
  } else {
    return defaultVal;
  }
}

bool ConfigManager::Read( const wxString& name, wxColour* ret ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlHandle parentHandle( e );
  TiXmlElement *c = ( TiXmlElement * ) parentHandle.FirstChild( cbU2C( key ) ).FirstChild( "colour" ).Element();
  if( c ) {
    const char *isNull = c->Attribute( "null" );
    if( isNull && strcmp( isNull, "true" ) == 0 ) {
      *ret = wxNullColour;
      return true;
    } else {
      int r, g, b;
      if( c->QueryIntAttribute( "r", &r ) == TIXML_SUCCESS
          && c->QueryIntAttribute( "g", &g ) == TIXML_SUCCESS
          && c->QueryIntAttribute( "b", &b ) == TIXML_SUCCESS ) {
        ret->Set( r, g, b );
        return true;
      }
    }
  }
  *ret = wxNullColour;
  return false;
}

void ConfigManager::Write( const wxString& name,  int value ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlElement *leaf = GetUniqElement( e, key );
  leaf->SetAttribute( "int", value );
}

int  ConfigManager::ReadInt( const wxString& name,  int defaultVal ) {
  int ret;
  if( Read( name, &ret ) ) {
    return ret;
  } else {
    return defaultVal;
  }
}

bool ConfigManager::Read( const wxString& name,  int* value ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlHandle parentHandle( e );
  TiXmlElement *leaf = parentHandle.FirstChild( cbU2C( key ) ).Element();
  if( leaf ) {
    return leaf->QueryIntAttribute( "int", value ) == TIXML_SUCCESS;
  }
  return false;
}

void ConfigManager::Write( const wxString& name,  bool value ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlElement *leaf = GetUniqElement( e, key );
  leaf->SetAttribute( "bool", value ? "1" : "0" );
}

bool  ConfigManager::ReadBool( const wxString& name,  bool defaultVal ) {
  bool ret;
  if( Read( name, &ret ) ) {
    return ret;
  } else {
    return defaultVal;
  }
}

bool ConfigManager::Read( const wxString& name,  bool* value ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlHandle parentHandle( e );
  TiXmlElement *leaf = parentHandle.FirstChild( cbU2C( key ) ).Element();
  if( leaf && leaf->Attribute( "bool" ) ) {
    *value = leaf->Attribute( "bool" )[0] == '1';
    return true;
  }
  return false;
}

void ConfigManager::Write( const wxString& name,  double value ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlElement *leaf = GetUniqElement( e, key );
  leaf->SetDoubleAttribute( "double", value );
}

double  ConfigManager::ReadDouble( const wxString& name,  double defaultVal ) {
  double ret;
  if( Read( name, &ret ) ) {
    return ret;
  } else {
    return defaultVal;
  }
}

bool ConfigManager::Read( const wxString& name,  double* value ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlHandle parentHandle( e );
  TiXmlElement *leaf = parentHandle.FirstChild( cbU2C( key ) ).Element();
  if( leaf ) {
    return leaf->QueryDoubleAttribute( "double", value ) == TIXML_SUCCESS;
  }
  return false;
}

void ConfigManager::Set( const wxString& name ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  GetUniqElement( e, key );
}

void ConfigManager::UnSet( const wxString& name ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlNode *leaf = GetUniqElement( e, key );
  e->RemoveChild( leaf );
}

bool ConfigManager::Exists( const wxString& name ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlHandle parentHandle( e );
  TiXmlElement *leaf = parentHandle.FirstChild( cbU2C( key ) ).Element();
  return leaf;
}

void ConfigManager::Write( const wxString& name,  const wxArrayString& arrayString ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlElement *leaf = GetUniqElement( e, key );
  TiXmlElement *as;
  as = GetUniqElement( leaf, _T( "astr" ) );
  leaf->RemoveChild( as );
  as = GetUniqElement( leaf, _T( "astr" ) );
  for( unsigned int i = 0; i < arrayString.GetCount(); ++i ) {
    TiXmlElement s( "s" );
    TiXmlText t( arrayString[i].mb_str( wxConvUTF8 ) );
    t.SetCDATA( true );
    s.InsertEndChild( t );
    as->InsertEndChild( s );
  }
}

void ConfigManager::Read( const wxString& name, wxArrayString *arrayString ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlHandle parentHandle( e );
  TiXmlNode *asNode = parentHandle.FirstChild( cbU2C( key ) ).FirstChild( "astr" ).Node();
  TiXmlNode *curr = nullptr;
  if( asNode ) {
    while( ( curr = asNode->IterateChildren( "s", curr ) ) ) {
      arrayString->Add( cbC2U( curr->FirstChild()->ToText()->Value() ) );
    }
  }
}

wxArrayString ConfigManager::ReadArrayString( const wxString& name ) {
  wxArrayString as;
  Read( name, &as );
  return as;
}

void ConfigManager::WriteBinary( const wxString& name,  const wxString& source ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlElement *str = GetUniqElement( e, key );
  TiXmlElement *s = GetUniqElement( str, _T( "bin" ) );
  s->SetAttribute( "crc", wxCrc32::FromString( source ) );
  SetNodeText( s, TiXmlText( wxBase64::Encode( source ).mb_str( wxConvUTF8 ) ) );
}

void ConfigManager::WriteBinary( const wxString& name,  void* ptr, size_t len ) {
  wxString s( ( wxChar* )ptr, len );
  WriteBinary( name,  s );
}

wxString ConfigManager::ReadBinary( const wxString& name ) {
  wxString str;
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  unsigned int crc = 0;
  TiXmlHandle parentHandle( e );
  TiXmlElement* bin = parentHandle.FirstChild( cbU2C( key ) ).FirstChild( "bin" ).Element();
  if( !bin ) {
    return wxEmptyString;
  }
  if( bin->QueryIntAttribute( "crc", ( int* )&crc ) != TIXML_SUCCESS ) {
    return wxEmptyString;
  }
  if( const TiXmlText* t = bin->FirstChild()->ToText() ) {
    str.assign( cbC2U( t->Value() ) );
    str = wxBase64::Decode( str );
    if( crc ==  wxCrc32::FromString( str ) ) {
      return str;
    }
  }
  return wxEmptyString;
}

wxArrayString ConfigManager::EnumerateSubPaths( const wxString& path ) {
  wxString key( path + _T( '/' ) );
  TiXmlNode* e = AssertPath( key );
  wxArrayString ret;
  TiXmlElement *curr = nullptr;
  if( e ) {
    while( e->IterateChildren( curr ) && ( curr = e->IterateChildren( curr )->ToElement() ) ) {
      wxChar c = *( cbC2U( curr->Value() ) );
      if( c < _T( 'A' ) || c > _T( 'Z' ) ) {
        ret.Add( cbC2U( curr->Value() ) );
      }
    }
  }
  return ret;
}

void ConfigManager::DeleteSubPath( const wxString& thePath ) {
  if( doc->ErrorId() ) {
    msging( wxString( _T( "### TinyXML error:\n" ) ) << cbC2U( doc->ErrorDesc() ) );
    doc->ClearError();
  }
  wxString path( thePath );
  to_lower( path );
  Collapse( path );
  wxString illegal( _T( " :.,;!\"\'$%&()[]<>{}?*+-|#" ) );
  size_t i;
  while( ( i = path.find_first_of( illegal ) ) != wxString::npos ) {
    path[i] = _T( '_' );
  }
  if( path.Last() == _T( '/' ) ) {
    path.RemoveLast();
  }
  if( path.IsSameAs( _T( "/" ) ) ) {
    return;
  }
  TiXmlElement* parent = pathNode ? pathNode : root;
  if( path.find( _T( '/' ) ) != wxString::npos ) {
    wxString sub;
    do {
      sub = path.BeforeFirst( _T( '/' ) );
      path = path.AfterFirst( _T( '/' ) );
      if( sub.IsEmpty() ) {
        parent = root;
      } else if( sub.IsSameAs( _T( "." ) ) )
        ;
      else if( parent != root && sub.IsSameAs( _T( ".." ) ) ) {
        parent = parent->Parent()->ToElement();
      } else {
        TiXmlElement* n = parent->FirstChildElement( cbU2C( sub ) );
        if( n ) {
          parent = n;
        } else
        { return; }
      }
    } while( path.find( _T( '/' ) ) != wxString::npos );
  }
  if( !path.IsEmpty() ) {
    if( TiXmlNode *toRemove = parent->FirstChild( cbU2C( path ) ) ) {
      toRemove->Clear();
      parent->RemoveChild( toRemove );
    }
  }
}

wxArrayString ConfigManager::EnumerateKeys( const wxString& path ) {
  wxString key( path + _T( '/' ) );
  TiXmlNode* e = AssertPath( key );
  wxArrayString ret;
  TiXmlElement *curr = nullptr;
  if( e ) {
    while( e->IterateChildren( curr ) && ( curr = e->IterateChildren( curr )->ToElement() ) ) {
      wxChar c = *( cbC2U( curr->Value() ) );
      if( c >= _T( 'A' ) && c <= _T( 'Z' ) ) {
        ret.Add( cbC2U( curr->Value() ) );
      }
    }
  }
  return ret;
}

void ConfigManager::Write( const wxString& name, const ISerializable& object ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlElement *obj = GetUniqElement( e, key );
  TiXmlElement *s = GetUniqElement( obj, _T( "obj" ) );
  SetNodeText( s, TiXmlText( cbU2C( wxBase64::Encode( object.SerializeOut() ) ) ) );
}

bool ConfigManager::Read( const wxString& name, ISerializable* object ) {
  wxString str;
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlHandle parentHandle( e );
  TiXmlText *t = ( TiXmlText * ) parentHandle.FirstChild( cbU2C( key ) ).FirstChild( "obj" ).FirstChild().Node();
  if( t ) {
    str.assign( cbC2U( t->Value() ) );
    object->SerializeIn( wxBase64::Decode( str ) );
  }
  return wxEmptyString;
}

void ConfigManager::Write( const wxString& name, const ConfigManagerContainer::StringToStringMap& map ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlElement *leaf = GetUniqElement( e, key );
  TiXmlElement *mNode;
  mNode = GetUniqElement( leaf, _T( "ssmap" ) );
  leaf->RemoveChild( mNode );
  mNode = GetUniqElement( leaf, _T( "ssmap" ) );
  for( ConfigManagerContainer::StringToStringMap::const_iterator it = map.begin(); it != map.end(); ++it ) {
    TiXmlElement s( cbU2C( it->first ) );
    TiXmlText t( cbU2C( it->second ) );
    t.SetCDATA( true );
    s.InsertEndChild( t );
    mNode->InsertEndChild( s );
  }
}

void ConfigManager::Read( const wxString& name, ConfigManagerContainer::StringToStringMap* map ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlHandle parentHandle( e );
  TiXmlNode *mNode = parentHandle.FirstChild( cbU2C( key ) ).FirstChild( "ssmap" ).Node();
  TiXmlNode *curr = nullptr;
  if( mNode ) {
    while( ( curr = mNode->IterateChildren( curr ) ) ) {
      ( *map )[cbC2U( curr->Value() )] = cbC2U( curr->FirstChild()->ToText()->Value() );
    }
  }
}

ConfigManagerContainer::StringToStringMap ConfigManager::ReadSSMap( const wxString& name ) {
  ConfigManagerContainer::StringToStringMap ret;
  Read( name, &ret );
  return ret;
}

void ConfigManager::Write( const wxString& name, const ConfigManagerContainer::IntToStringMap& map ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlElement *leaf = GetUniqElement( e, key );
  TiXmlElement *mNode;
  mNode = GetUniqElement( leaf, _T( "ismap" ) );
  leaf->RemoveChild( mNode );
  mNode = GetUniqElement( leaf, _T( "ismap" ) );
  wxString tmp;
  for( ConfigManagerContainer::IntToStringMap::const_iterator it = map.begin(); it != map.end(); ++it ) {
    tmp.Printf( _T( "x%d" ), ( int ) it->first );
    TiXmlElement s( tmp.mb_str() );
    TiXmlText t( cbU2C( it->second ) );
    t.SetCDATA( true );
    s.InsertEndChild( t );
    mNode->InsertEndChild( s );
  }
}

void ConfigManager::Read( const wxString& name, ConfigManagerContainer::IntToStringMap* map ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlHandle parentHandle( e );
  TiXmlNode *mNode = parentHandle.FirstChild( cbU2C( key ) ).FirstChild( "ismap" ).Node();
  TiXmlNode *curr = nullptr;
  long tmp;
  if( mNode ) {
    while( ( curr = mNode->IterateChildren( curr ) ) ) {
      cbC2U( curr->Value() ).Mid( 1 ).ToLong( &tmp );
      ( *map )[tmp] = cbC2U( curr->FirstChild()->ToText()->Value() );
    }
  }
}

ConfigManagerContainer::IntToStringMap ConfigManager::ReadISMap( const wxString& name ) {
  ConfigManagerContainer::IntToStringMap ret;
  Read( name, &ret );
  return ret;
}

void ConfigManager::Write( const wxString& name, const ConfigManagerContainer::StringSet& set ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlElement *leaf = GetUniqElement( e, key );
  TiXmlElement *mNode;
  mNode = GetUniqElement( leaf, _T( "sset" ) );
  leaf->RemoveChild( mNode );
  mNode = GetUniqElement( leaf, _T( "sset" ) );
  for( ConfigManagerContainer::StringSet::const_iterator it = set.begin(); it != set.end(); ++it ) {
    TiXmlElement s( "s" );
    TiXmlText t( cbU2C( *it ) );
    t.SetCDATA( true );
    s.InsertEndChild( t );
    mNode->InsertEndChild( s );
  }
}

void ConfigManager::Read( const wxString& name, ConfigManagerContainer::StringSet* set ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlHandle parentHandle( e );
  TiXmlNode *mNode = parentHandle.FirstChild( cbU2C( key ) ).FirstChild( "sset" ).Node();
  TiXmlNode *curr = nullptr;
  if( mNode ) {
    while( ( curr = mNode->IterateChildren( curr ) ) ) {
      set->insert( cbC2U( curr->FirstChild()->ToText()->Value() ) );
    }
  }
}

ConfigManagerContainer::StringSet ConfigManager::ReadSSet( const wxString& name ) {
  ConfigManagerContainer::StringSet ret;
  Read( name, &ret );
  return ret;
}

void ConfigManager::Write( const wxString& name, const ConfigManagerContainer::SerializableObjectMap* map ) {
  wxString key( name );
  TiXmlElement* e = AssertPath( key );
  TiXmlElement *leaf = GetUniqElement( e, key );
  TiXmlElement *mNode;
  mNode = GetUniqElement( leaf, _T( "objmap" ) );
  leaf->RemoveChild( mNode );
  mNode = GetUniqElement( leaf, _T( "objmap" ) );
  for( ConfigManagerContainer::SerializableObjectMap::const_iterator it = map->begin(); it != map->end(); ++it ) {
    TiXmlElement s( cbU2C( it->first ) );
    s.InsertEndChild( TiXmlText( cbU2C( wxBase64::Encode( it->second->SerializeOut() ) ) ) );
    mNode->InsertEndChild( s );
  }
}

void ConfigManager::InitPaths() {
  ConfigManager::config_folder = ConfigManager::GetUserDataFolder();
  ConfigManager::home_folder = wxStandardPathsBase::Get().GetUserConfigDir();
  ConfigManager::app_path = ::DetermineExecutablePath();
  wxString res_path = ::DetermineResourcesPath();
  if( data_path_global.IsEmpty() ) {
    ConfigManager::data_path_global = wxStandardPathsBase::Get().GetDataDir();
  } else {
    ConfigManager::data_path_global = UnixFilename( data_path_global );
  }
  wxString dataPathUser = ConfigManager::config_folder + wxFILE_SEP_PATH + _T( "share" );
  if( !has_alternate_user_data_path ) {
    dataPathUser = wxString::FromUTF8( g_build_filename( g_get_user_data_dir(), NULL ) );
  }
  ConfigManager::data_path_user = dataPathUser + wxFILE_SEP_PATH + _T( "m" );
  if( wxFileName( ConfigManager::data_path_user ) == wxFileName( ConfigManager::data_path_global ) ) {
    ConfigManager::data_path_user.append( _T( "." ) + wxGetUserId() );
  }
  CreateDirRecursively( ConfigManager::config_folder );
  CreateDirRecursively( ConfigManager::data_path_user   + _T( "/plugins/" ) );
  CreateDir( ConfigManager::data_path_user   + _T( "/scripts/" ) );
  ConfigManager::temp_folder = wxStandardPathsBase::Get().GetTempDir();
}

void ConfigManager::MigrateFolders() {
  if( !wxDirExists( wxStandardPaths::Get().GetUserDataDir() ) ) {
    return;
  }
  wxString newConfigFolder = wxString::FromUTF8( g_build_filename( g_get_user_config_dir(), "codeblocks", NULL ) );
  if( wxDirExists( newConfigFolder ) ) {
    return;
  }
  wxString oldConfigFolder = wxStandardPaths::Get().GetUserDataDir();
  wxString oldDataFolder = oldConfigFolder + wxFILE_SEP_PATH + _T( "share" ) + wxFILE_SEP_PATH + _T( "codeblocks" );
  wxString newDataFolder = wxString::FromUTF8( g_build_filename( g_get_user_data_dir(), NULL ) ) + wxFILE_SEP_PATH + _T( "codeblocks" );
  wxString msg;
  msg = F( _T( "存储配置文件和用户数据文件的位置\n"
               "已改为更符合标准.\n"
               "\n"
               "Now moving \"%s\"\n"
               "to \"%s\"\n"
               "and \"%s\"\n"
               "to \"%s\".\n" ),
           oldDataFolder.wx_str(),
           newDataFolder.wx_str(),
           oldConfigFolder.wx_str(),
           newConfigFolder.wx_str() );
  msging( msg, _T( "Try to migrate config-folder ..." ), wxICON_INFORMATION );
  bool success = true;
  if( wxDirExists( oldDataFolder ) ) {
    CreateDirRecursively( newDataFolder );
    success = wxRenameFile( oldDataFolder, newDataFolder );
    wxRmdir( oldConfigFolder + wxFILE_SEP_PATH + _T( "share" ) );
  }
  if( success ) {
    CreateDirRecursively( newConfigFolder );
    success = wxRenameFile( oldConfigFolder, newConfigFolder );
  }
  if( !success ) {
    msg = F( _T( "Error moving \"%s\"\n"
                 "to \"%s\"\n"
                 "or \"%s\"\n"
                 "to \"%s\".\n\n"
                 "Please check the folders manually (access rights?) !\n"
                 "A new configuration will be created from scratch!" ),
             oldDataFolder.wx_str(),
             newDataFolder.wx_str(),
             oldConfigFolder.wx_str(),
             newConfigFolder.wx_str() );
    msging( msg, _T( "迁移配置文件夹时出错 ..." ), wxICON_ERROR );
  }
}

void ConfigManagerWrapper::Write( const wxString& name, const wxString& value, bool ignoreEmpty ) {
  if( m_namespace.empty() ) {
    return;
  }
  ConfigManager *c = Manager::Get()->GetConfigManager( m_namespace );
  c->Write( m_basepath + name, value, ignoreEmpty );
}

wxString ConfigManagerWrapper::Read( const wxString& key, const wxString& defaultVal ) {
  if( m_namespace.empty() ) {
    return defaultVal;
  }
  ConfigManager *c = Manager::Get()->GetConfigManager( m_namespace );
  return c->Read( m_basepath + key, defaultVal );
}

bool ConfigManagerWrapper::Read( const wxString& key, wxString* str ) {
  if( m_namespace.empty() ) {
    return false;
  }
  ConfigManager *c = Manager::Get()->GetConfigManager( m_namespace );
  return c->Read( key, str );
}

void ConfigManagerWrapper::Write( const wxString& key, const char* str ) {
  if( m_namespace.empty() ) {
    return;
  }
  ConfigManager *c = Manager::Get()->GetConfigManager( m_namespace );
  c->Write( key, str );
}

void ConfigManagerWrapper::Write( const wxString& name, int value ) {
  if( m_namespace.empty() ) {
    return;
  }
  ConfigManager *c = Manager::Get()->GetConfigManager( m_namespace );
  c->Write( m_basepath + name, value );
}
bool ConfigManagerWrapper::Read( const wxString& name, int* value ) {
  if( m_namespace.empty() ) {
    return false;
  }
  ConfigManager *c = Manager::Get()->GetConfigManager( m_namespace );
  return c->Read( m_basepath + name, value );
}

int ConfigManagerWrapper::ReadInt( const wxString& name, int defaultVal ) {
  if( m_namespace.empty() ) {
    return defaultVal;
  }
  ConfigManager *c = Manager::Get()->GetConfigManager( m_namespace );
  return c->ReadInt( m_basepath + name, defaultVal );
}

void ConfigManagerWrapper::Write( const wxString& name, bool value ) {
  if( m_namespace.empty() ) {
    return;
  }
  ConfigManager *c = Manager::Get()->GetConfigManager( m_namespace );
  c->Write( m_basepath + name, value );
}
bool ConfigManagerWrapper::Read( const wxString& name, bool* value ) {
  if( m_namespace.empty() ) {
    return false;
  }
  ConfigManager *c = Manager::Get()->GetConfigManager( m_namespace );
  return c->Read( m_basepath + name, value );
}
bool ConfigManagerWrapper::ReadBool( const wxString& name, bool defaultVal ) {
  if( m_namespace.empty() ) {
    return defaultVal;
  }
  ConfigManager *c = Manager::Get()->GetConfigManager( m_namespace );
  return c->ReadBool( m_basepath + name, defaultVal );
}

void ConfigManagerWrapper::Write( const wxString& name, double value ) {
  if( m_namespace.empty() ) {
    return;
  }
  ConfigManager *c = Manager::Get()->GetConfigManager( m_namespace );
  c->Write( m_basepath + name, value );
}
bool ConfigManagerWrapper::Read( const wxString& name, double* value ) {
  if( m_namespace.empty() ) {
    return false;
  }
  ConfigManager *c = Manager::Get()->GetConfigManager( m_namespace );
  return c->Read( m_basepath + name, value );
}
double ConfigManagerWrapper::ReadDouble( const wxString& name, double defaultVal ) {
  if( m_namespace.empty() ) {
    return defaultVal;
  }
  ConfigManager *c = Manager::Get()->GetConfigManager( m_namespace );
  return c->ReadDouble( m_basepath + name, defaultVal );
}

wxArrayString cbReadBatchBuildPlugins() {
  ConfigManager *bbcfg = Manager::Get()->GetConfigManager( _T( "batch_build_plugins" ) );
  wxArrayString bbplugins = bbcfg->ReadArrayString( _T( "/batch_build_plugins" ) );
  if( !bbplugins.GetCount() ) {
    bbplugins.Add( CompilerPluginFilename );
  }
  return bbplugins;
}

void cbWriteBatchBuildPlugins( wxArrayString bbplugins, wxWindow *messageBoxParent ) {
  const wxString &compiler = CompilerPluginFilename;
  if( bbplugins.Index( compiler ) == wxNOT_FOUND ) {
    bbplugins.Add( compiler );
    InfoWindow::Tip( _T( "编译器插件必须始终为shell加载!自动重新启用" ), _( "错误" ), 3500 );
  }
  ConfigManager *bbcfg = Manager::Get()->GetConfigManager( _T( "plugins" ) );
  bbcfg->Write( _T( "/batch_build_plugins" ), bbplugins );
}
